package dao

import (
	"context"
	"errors"
	"github.com/go-sql-driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"time"
)

type InteractiveDao interface {
	Get(ctx context.Context, biz string, id int64) (InteractiveModel, error)
	GetLikedInfo(ctx context.Context, biz string, id, uid int64) (UserLikeBizModel, error)
	GetCollectionInfo(ctx context.Context, biz string, id, uid int64) (UserCollectBizModel, error)
	IncrLiked(ctx context.Context, biz string, id, uid int64) error
	IncrDisLiked(ctx context.Context, biz string, id, uid int64) error
	QueryCollectList(ctx context.Context, id int64, limit, offset int) ([]CollectionModle, error)
	InsertCollect(ctx context.Context, uid int64, cname string, desc string, isPub bool) (int64, error)
	IncrReadCnt(ctx context.Context, biz string, bizId int64) error
	BatchIncrReadCnt(ctx context.Context, bizs []string, bizIds []int64) error
	InsertCollectInfo(ctx context.Context, biz string, uid, cid, bizId int64) error
}

type interactiveDao struct {
	db *gorm.DB
}

func NewInteractiveDao(db *gorm.DB) InteractiveDao {
	return &interactiveDao{db: db}
}

func (d *interactiveDao) InsertCollectInfo(ctx context.Context, biz string, uid, cid, bizId int64) error {
	err := d.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		err := tx.Model(&UserCollectBizModel{}).Clauses(clause.OnConflict{DoUpdates: clause.Assignments(map[string]interface{}{
			"updated_at": time.Now(),
		}),
		}).Create(&UserCollectBizModel{
			BizId: bizId,
			Biz:   biz,
			Uid:   uid,
			Cid:   cid,
		}).Error
		if err != nil {
			return err
		}

		return tx.Model(&InteractiveModel{}).Clauses(clause.OnConflict{DoUpdates: clause.Assignments(map[string]interface{}{
			"updated_at": time.Now(),
			"CollectCnt": gorm.Expr("collect_cnt+1"),
		})}).Create(&InteractiveModel{
			BizId:      bizId,
			Biz:        biz,
			CollectCnt: 1,
		}).Error
	})
	return err
}

func (d *interactiveDao) BatchIncrReadCnt(ctx context.Context, bizs []string, bizIds []int64) error {
	//批处理，多个就需要每个使用一个实例
	err := d.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		txDao := NewInteractiveDao(tx)
		for i := 0; i < len(bizIds); i++ {
			err := txDao.IncrReadCnt(ctx, bizs[i], bizIds[i])
			if err != nil {
				return err
			}
		}
		return nil
	})
	return err
}

func (d *interactiveDao) IncrReadCnt(ctx context.Context, biz string, bizId int64) error {
	data := InteractiveModel{
		BizId:   bizId,
		Biz:     biz,
		ReadCnt: 1,
	}
	err := d.db.WithContext(ctx).Clauses(clause.OnConflict{Columns: []clause.Column{{Name: "id"}}, DoUpdates: clause.Assignments(map[string]any{
		"updated_at": time.Now(),
		"read_cnt":   gorm.Expr("read_cnt+1"),
	})}).Create(&data).Error
	return err
}

func (d *interactiveDao) InsertCollect(ctx context.Context, uid int64, cname string, desc string, isPub bool) (int64, error) {
	data := CollectionModle{
		UserId:      uid,
		CName:       cname,
		Description: desc,
		IsPub:       isPub,
	}
	err := d.db.WithContext(ctx).Create(&data).Error
	if sqlError, ok := err.(*mysql.MySQLError); ok {
		const uniqueConflictsErrNo uint16 = 1062
		if sqlError.Number == uniqueConflictsErrNo {
			return 0, errors.New("收藏夹名字冲突")
		}
	}
	return int64(data.ID), err
}

func (d *interactiveDao) QueryCollectList(ctx context.Context, id int64, limit, offset int) ([]CollectionModle, error) {
	var model []CollectionModle
	err := d.db.WithContext(ctx).Model(&CollectionModle{}).Where("user_id = ? ", id).Offset(offset).Limit(limit).Order("sort DESC").Find(&model).Error
	return model, err
}

func (d *interactiveDao) IncrLiked(ctx context.Context, biz string, id, uid int64) error {
	//必须同时更新两个表，一个喜欢的状态，一个是数量，使用事务，并且需要检测是否存在的，需要初始化和后续的叠加
	err := d.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		err := d.db.WithContext(ctx).Clauses(clause.OnConflict{DoUpdates: clause.Assignments(map[string]interface{}{
			"UpdatedAt": time.Now(),
		}),
		}).Create(&UserLikeBizModel{
			BizId: id,
			Biz:   biz,
			Uid:   uid,
		}).Error
		if err != nil {
			return err
		}
		return d.db.WithContext(ctx).Clauses(clause.OnConflict{DoUpdates: clause.Assignments(map[string]interface{}{
			"LikeCnt":   gorm.Expr("like_cnt+1"),
			"UpdatedAt": time.Now(),
		}),
		}).Create(&InteractiveModel{
			BizId:   id,
			Biz:     biz,
			LikeCnt: 1,
		}).Error
	})
	return err
}

func (d *interactiveDao) IncrDisLiked(ctx context.Context, biz string, id, uid int64) error {
	//取消删除代表已经有记录了，不会需要检查时是否存在出现check do的问题，就不需要使用冲突，OnConflict和DoUpdates
	//进行软删除，DeletedAt `gorm:"index"`，根据这个字段是否为null进行筛选条件
	err := d.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		err := d.db.WithContext(ctx).Model(&UserLikeBizModel{}).Where("biz_id = ? AND biz = ? AND uid = ?", id, biz, uid).Updates(map[string]any{
			"UpdatedAt": time.Now(),
			"DeletedAt": time.Now(),
		}).Error
		if err != nil {
			return err
		}
		return d.db.WithContext(ctx).Model(&InteractiveModel{}).Where("biz_id = ? AND biz = ?", id, biz).Updates(map[string]any{
			"LikeCnt":   gorm.Expr("like_cnt-1"),
			"UpdatedAt": time.Now(),
		}).Error
	})
	return err
}

func (d *interactiveDao) Get(ctx context.Context, biz string, id int64) (InteractiveModel, error) {
	var data InteractiveModel
	err := d.db.WithContext(ctx).Model(&InteractiveModel{}).Where("biz_id=? AND biz=?", biz, id).First(&data).Error
	return data, err
}

func (d *interactiveDao) GetLikedInfo(ctx context.Context, biz string, id, uid int64) (UserLikeBizModel, error) {
	var data UserLikeBizModel
	err := d.db.WithContext(ctx).Model(&UserLikeBizModel{}).Where("biz_id=? AND biz=? AND uid=?", biz, id, uid).First(&data).Error
	return data, err
}

func (d *interactiveDao) GetCollectionInfo(ctx context.Context, biz string, id, uid int64) (UserCollectBizModel, error) {
	var data UserCollectBizModel
	err := d.db.WithContext(ctx).Model(&UserCollectBizModel{}).Where("biz_id=? AND biz=? AND uid=?", biz, id, uid).First(&data).Error
	return data, err
}
